package cn.taobaoke.restful.controller.agent;

import cn.taobaoke.restful.controller.BaseController;
import cn.taobaoke.restful.domain.AgentEntity;
import cn.taobaoke.restful.domain.InviteCodeEntity;
import cn.taobaoke.restful.domain.PIDEntity;
import cn.taobaoke.restful.domain.WxGroupEntity;
import cn.taobaoke.restful.exception.TBKException;
import cn.taobaoke.restful.interceptor.Auth;
import cn.taobaoke.restful.service.*;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.List;

/**
 * @author yangzuo
 */
@RestController
@RequestMapping(value = "/{domain}/{domainId}/api/agents")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class AgentController extends BaseController {

    @Auth(type = {Auth.AuthType.AGENT, Auth.AuthType.ADMIN})
    @GetMapping("")
    public Result getAgentListForAdmin(@PathVariable Long domainId, String keyword, String type, Integer page, Integer pageSize) {
        logger.debug("查询代理列表操作: GET /{}/api/agents?keyword={}&type={}&page={}&pageSize={}", domainId, keyword, type, page, pageSize);
        Pageable pageable = createPageable(page, pageSize);
        Page<AgentEntity> agentEntityList = agentService.search(keyword, type, pageable, domainId);

        // 注意: 这里查询的二级代理时候,需要将对应的一级代理附加上
        agentService.addAgent2ParentAgent(agentEntityList.getContent(), domainId);
        return wrapPage(pageable, agentEntityList);
    }

    @Auth(type = {Auth.AuthType.AGENT, Auth.AuthType.ADMIN})
    @GetMapping("/{agentId}/class2")
    public Result getSubAgents(@PathVariable Long domainId, @PathVariable Long agentId, String keyword, Integer page, Integer pageSize) {
        logger.debug("查询代理列表操作: GET /{}/api/agents/{}/class2?keyword={}&page={}&pageSize={}", domainId, agentId, keyword, page, pageSize);
        Pageable pageable = createPageable(page, pageSize);
        Page<AgentEntity> agentEntityList = agentService.searchForAgent(keyword, agentId, pageable, domainId);
        return wrapPage(pageable, agentEntityList);
    }


    @GetMapping("/searchChildren")
    public Result searchChildren(@PathVariable Long domainId, Long agentId, String keyword, Integer page, Integer pageSize) {
        logger.debug("查询一级代理列表操作: GET /{}/api/agents/searchChildren?agentId={}&keyword={}&page={}&pageSize={}", domainId, agentId, keyword, page, pageSize);

        Pageable pageable = createPageable(page, pageSize);
        Page<AgentEntity> agentEntities = agentService.searchChildren(agentId, keyword, pageable, domainId);
        return wrapPage(pageable, agentEntities);
    }

    @GetMapping("/getAllChildren")
    public Result getAllChildren(@PathVariable Long domainId, Long agentId) {
        logger.debug("查询一级代理列表操作: GET /{}/api/agents/getAllChildren?agentId={}", domainId, agentId);
        List<AgentEntity> agentEntities = agentService.getAllChildren(agentId, domainId);
        return wrapObj(agentEntities);
    }

    // 获取所有的后代节点, 分页获取
    @GetMapping("/descendants")
    public Result getDescendants(@PathVariable Long domainId, Long agentId, String keyword, Integer page, Integer pageSize) {
        logger.debug("查询所有的后代: GET /{}/api/agents/descendants?agentId={}&keyword={}&page={}&pageSize={}", domainId, agentId, keyword, page, pageSize);

        Pageable pageable = createPageable(page, pageSize);
        Page<AgentEntity> agentEntityList = agentService.getDescendantByPage(agentId, keyword, pageable, domainId);
        return wrapPage(pageable, agentEntityList);
    }

    @Auth(type = {Auth.AuthType.AGENT, Auth.AuthType.ADMIN})
    @GetMapping("/all")
    public Result getAllAgents(@PathVariable Long domainId) {
        logger.debug("GET /{}/api/agents/all", domainId);

        List<AgentEntity> agents = agentService.getAllAgents(domainId);
        return wrapObj(agents);
    }

    /**
     * 保存代理
     *
     * @param agentEntity
     * @return
     * @notice 保存代理时注意: 谁创建, 会负责父子关系设置
     */
    @Auth(type = {Auth.AuthType.AGENT, Auth.AuthType.ADMIN})
    @PutMapping("")
    public Result saveAgent(@PathVariable Long domainId, @RequestBody AgentEntity agentEntity) {
        logger.debug("更新代理操作: PUT /{}/api/agents \n{}", domainId, agentEntity);

        InviteCodeEntity inviteCodeEntity = null;
        String inviteCode = agentEntity.getInviteCode();

        if (inviteCode != null) {
            inviteCodeEntity = inviteCodeService.findByCode(inviteCode);
            if (inviteCodeEntity == null) {
                return wrapError("非有效的邀请码 " + inviteCode);
            }
            if (inviteCodeEntity.getIsAssigned() != null && inviteCodeEntity.getIsAssigned()) {
                return wrapError("邀请码已被注册");
            }
            Long parentId = inviteCodeEntity.getOwner();
            agentEntity.setParentId(parentId);

            logger.info("invite code => {}  parentId => {}", inviteCode, parentId);
        }

        if (agentEntity.getId() == null) {
            // 创建时候, 需要新分配PID
            if (agentEntity.getPid() != null) {
                // 若是外部提供了PID,则直接添加到表中
                agentEntity = agentService.insertAgentEntity(agentEntity, domainId);
                String pid = agentEntity.getPid();
                pidService.savePid(pid, "manual", agentEntity.getId());
            } else {
                PIDEntity pidEntity = pidService.getFirstAvailablePID(domainId);
                agentEntity.setPid(pidEntity.getPid());
                pidEntity.setIsAssigned(true);
                agentEntity = agentService.insertAgentEntity(agentEntity, domainId);
                pidEntity.setAgentId(agentEntity.getId());
                pidService.updatePID(pidEntity);

                if (inviteCode != null) {
                    logger.info("start to consume inviteCode => {}, agentId => {}", inviteCode, agentEntity.getInviteCode());
                    inviteCodeService.consumeCode(agentEntity.getId(), inviteCode);
                }
                if (inviteCodeEntity != null) {
                    agentEntity.setDomain(inviteCodeEntity.getDomain());
                }
            }
        } else {
            agentEntity = agentService.updateAgentEntity(agentEntity, domainId);
        }
        return wrapObj(agentEntity);
    }

    @Auth(type = Auth.AuthType.AGENT)
    @PutMapping("/{agentId}/subAgent")
    public Result createOrSaveSubAgent(@PathVariable Long domainId, @PathVariable Long agentId, @RequestBody AgentEntity agentEntity) throws IOException {
        logger.debug("保存下级代理信息: PUT /{}/api/agents\n {}", domainId, agentEntity);

        // 检查邀请码
        if (agentEntity.getInviteCode() != null) {
            // 检查邀请码的有效性
            String code = agentEntity.getInviteCode();
            Boolean ok = innerAPIService.useInviteCode(code);
            if (ok == false) {
                throw new TBKException("无效的邀请码");
            }
        }
        agentEntity = agentService.saveAgentSubAgent(agentId, agentEntity, domainId);
        return wrapObj(agentEntity);
    }

    @Auth(type = {Auth.AuthType.AGENT, Auth.AuthType.ADMIN})
    @GetMapping("/{agentId}")
    public Result getAgentById(@PathVariable Long domainId, @PathVariable Long agentId) {
        logger.debug("查询代理操作: GET /{}/api/agents/{}", domainId, agentId);
        AgentEntity agentEntity = agentService.getAgentById(agentId, domainId);
        return wrapObj(agentEntity);
    }

    /**
     * @param agentId
     * @param wxGroupEntity
     * @return
     */
    @Auth(type = {Auth.AuthType.AGENT, Auth.AuthType.ADMIN})
    @PostMapping("/{agentId}/groups")
    public Result addGroupEntity(@PathVariable Long domainId, @PathVariable Long agentId, @RequestBody WxGroupEntity wxGroupEntity) {
        logger.debug("POST /{}/api/agents/{}/groups \n{}", domainId, agentId, wxGroupEntity);

        agentService.checkAgentExistById(agentId, domainId);
        wxGroupEntity.setAgentId(agentId);
        wxGroupService.addWxGroupEntity(wxGroupEntity, false, domainId);
        return wrapVoid();
    }

    @Auth(type = {Auth.AuthType.AGENT, Auth.AuthType.ADMIN})
    @DeleteMapping("/{proxyId}")
    public Result deleteProxy(@PathVariable Long domainId, @PathVariable Long proxyId) {
        logger.debug("DELETE /{}/api/agents/{}", domainId, proxyId);
        agentService.deleteAgentEntity(proxyId, domainId);
        return wrapVoid();
    }


    private final
    @NonNull
    InviteCodeService inviteCodeService;

    private final
    @NonNull
    InnerAPIService innerAPIService;

    private final
    @NonNull
    WxGroupService wxGroupService;

    private final
    @NonNull
    AgentService agentService;

    private final
    @NonNull
    PIDService pidService;

    private static Logger logger = LogManager.getLogger(AgentController.class);
}
